package com.pnnd.stream;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

public class SingleSegmentInputStream extends InputStream {

    private int mark;
    private int offset;
    private MemorySegment memorySegment;

    public SingleSegmentInputStream(MemorySegment segment){
        this.memorySegment = segment;
        this.mark = this.offset = 0;
    }

    @Override
    public int read() throws IOException {

        if(memorySegment.end() == this.offset){
            return -1;
        }

        byte v = this.memorySegment.get(this.offset);
        this.offset += 1;
        return (int)v;
    }

    @Override
    public int read(byte b[], int off, int len) throws IOException {
        if(this.memorySegment.end() == this.offset){
            throw new EOFException();
        }

        int readLen = Math.min(this.offset+len,(int)this.memorySegment.end()-this.offset);

        this.memorySegment.get(this.offset,b,off,readLen);

        this.offset += readLen;

        return readLen;
    }

    @Override
    public long skip(long n) throws IOException {

        int readLen = Math.min(this.offset+(int)n,(int)this.memorySegment.end()-this.offset);

        this.offset += readLen;

        return readLen;
    }

    @Override
    public int available() throws IOException {
        return (int)(this.memorySegment.end() - this.offset);
    }

    @Override
    public boolean markSupported() {
        return true;
    }

    @Override
    public synchronized void mark(int readlimit) {

        if(readlimit > this.offset || readlimit<this.memorySegment.start()){
            throw new IndexOutOfBoundsException();
        }

        this.mark = readlimit;
    }

    @Override
    public synchronized void reset() throws IOException {
        this.offset = mark;
    }

    @Override
    public void close(){

        if(!this.memorySegment.getOwner().equals(this)){
            throw new RuntimeException("error owner close!!!");
        }

        this.memorySegment.free();
    }

    @Override
    public boolean equals(Object other){
        if(!(other instanceof SingleSegmentInputStream)){
            return false;
        }

        SingleSegmentInputStream singleSegmentInputStream = (SingleSegmentInputStream) other;

        return singleSegmentInputStream.memorySegment.equals(this)
                && this.offset == singleSegmentInputStream.offset
                && this.mark == singleSegmentInputStream.mark;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mark, offset, memorySegment);
    }
}
